package smartfleet.mobile;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;

import smartfleet.mobile.Protocol.Arrival;
import smartfleet.mobile.Protocol.BdMsg;
import smartfleet.mobile.Protocol.Charge;
import smartfleet.mobile.Protocol.Msg;
import smartfleet.mobile.Protocol.Trip;
import smartfleet.mobile.Protocol.altitude;
import smartfleet.mobile.rest.webservice.sync.database.DatabaseSyncWebServiceUtil;

import android.app.Service;
import android.content.Intent;
import android.location.Location;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.widget.Toast;

public class SimService extends Service{

	private Socket SockSimulator;
	
	private PrintWriter OutSimulator;
	
	private BufferedReader InSimulator;

	private String lastStation_addr;
	
	private int lastStation_port;

	private Messenger main;
	
	private dbAdapter mDbHelper;
	
	private int _id;
	
	RegisterValues reg;
	
	private String _address;
	
	private int _port;
	
	private SimThread simThread = new SimThread();
	
	private NetServer netServer;
	
	final Messenger mMessenger = new Messenger(new IncomingHandler());
	
	public static final int Register_Mobile = 1;
	public static final int Mobile_Move = 2;
	public static final int Unregister_Mobile = 3;
	public static final int Send_DB = 4;
	public static final int Charged = 5;
	public static final int Reset_Battery = 6;
	
	public static final int trip_msg = 1;
	public static final int arrival_msg = 2;
	public static final int charge_msg = 3;
	public static final int Bd_msg = 4;
	public static final int Change_Altitude = 5;

	
	class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            	case Register_Mobile:
            		
            		reg = (RegisterValues) msg.obj;
            		
            		main = msg.replyTo;
            		_id = reg.get_id();
            		_port = reg.get_netport();
            		
            		simThread.setAddress(reg.get_sim_addr());
            		simThread.setPort(reg.get_sim_port());
            		simThread.start();
            		/** RegisterVehicle <id> <netport> <emulport> <battery> <StationId> **/
            		//Register(_id, _port, reg.get_emulport(), reg.getBattery(), reg.get_station_id());
            		break;
            	case Mobile_Move:
            		sendMove(((Location) msg.obj).getLongitude(), ((Location) msg.obj).getLatitude());
            		break;
            	case Unregister_Mobile:
            		Unregister(msg.arg1);
            		break;
            	case Send_DB:
            		mDbHelper = (dbAdapter) msg.obj;
            		break;
            	case Charged:
            		setBattery();
            		sendArrivalMessage(lastStation_addr, lastStation_port);
            		break;
            	case Reset_Battery:
            		resetBattery();
            		break;
                default:
                    super.handleMessage(msg);
            }
        }
    }
	

    @Override
    public IBinder onBind(Intent intent) {
    	Toast.makeText(this, "Simlulator service started", Toast.LENGTH_SHORT).show();
    	return mMessenger.getBinder();
    }
    
    @Override
    public void onCreate() {
        super.onCreate();
        
        netServer = new NetServer(10000);
        netServer.start();
    }
    
    @Override
    public void onDestroy() {
        
    	netServer.close();
    	simThread.stop();
        try {
        	
			SockSimulator.close();
			
		} catch (IOException e) {
			e.printStackTrace();
		}
		

    }
    
    private String treatAddress(String add){
    	
    	if(add.equals("127.0.0.1"))
    		return reg.get_sim_addr();
    	else if(add.equals(_address))
    		return "10.0.2.2";
    		else return add;
    	
    }
    
    
    private void Register(int id, int netport, int emulport, int batt, int sta_id){
    	
    	/** RegisterVehicle <id> <netport> <emulport> <battery> <StationId> **/
    	
    	OutSimulator.println("RegisterVehicle "+id+" "+netport+" "+emulport+" "+batt+" "+sta_id);
    	
    }
    
    private void Unregister(int id){
    	
    	OutSimulator.println("UnregisterVehicle "+id);
    }
    
    private void sendMove(double lon, double lat){
    	
    	OutSimulator.println("Move "+lon+" "+lat);
    	
    }
    
    private void setBattery(){
    	
    	System.out.println("simService: "+ mDbHelper.getBattery());
    	OutSimulator.println("Set_Battery "+mDbHelper.getBattery());
    }
    
    private void riseAltitude(){
    	
    	OutSimulator.println("Rise_Altitude");
    }
    
    private void normalize(){
    	
    	OutSimulator.println("Normalize");
    }
    
    private void resetBattery(){
    	
    	OutSimulator.println("Reset_Battery");
    }
    
    
    private void sendInformation(int id, String address, int port){

    	try {
    		
			Socket vehicleSock = new Socket(address, port);
			
			OutputStream test = vehicleSock.getOutputStream();
			
			BdMsg bdmsg = mDbHelper.getDB();
			
			Msg.Builder msg = Msg.newBuilder();
			msg.setId(Bd_msg);
			msg.setBdMsg(bdmsg);
			
			msg.build().writeTo(test);
			
			vehicleSock.close();
			
			
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
    	
    	
    }
    
    private void sendArrivalMessage(String address, int port){
    	
    	Msg.Builder message = Msg.newBuilder();
    	message.setId(arrival_msg);
    	
    	Arrival.Builder a = Arrival.newBuilder();
    	a.setId(_id);
    	a.setBattery(mDbHelper.getBattery());
    	a.setAddress(_address);
    	a.setPort(_port);
    	
    	message.setArrival(a);
    	
    	Socket Sock;
		try {
			
			Sock = new Socket(address, port);
			
			OutputStream out = Sock.getOutputStream();
			
			message.build().writeTo(out);
			
			Sock.close();
			
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
    	
    }
    
    private void sendAltitudeChange(int id, String address, int port)
    {
    	
    	Msg.Builder message = Msg.newBuilder();
    	altitude.Builder alt = altitude.newBuilder(); 
    	
    	message.setId(Change_Altitude);
    	alt.setAlt(mDbHelper.getAltitude());
    	alt.setId(_id);
    	
    	message.setAltitude(alt);
    	
    	Socket vehicleSock;
		try {
			
			
			vehicleSock = new Socket(address, port);
			
			OutputStream out = vehicleSock.getOutputStream();
			
			message.build().writeTo(out);
			
			vehicleSock.close();
			
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
    	
    }
    
    /** Class SimThread **/
    
    class SimThread extends Thread{
    	
    	String address;
    	
    	int port;
    	
    	public SimThread(){
    		
    	}
    	
    	public void setPort(int p){
    		this.port = p;
    	}
    	
    	public void setAddress(String a){
    		this.address = a;
    	}
    	
    	public void run(){
			
			String inline;
			String[] split;
				
				try {
					
					SockSimulator = new Socket(address, port);
					OutSimulator = new PrintWriter(SockSimulator.getOutputStream(), true);
					InSimulator = new BufferedReader(new InputStreamReader(SockSimulator.getInputStream()));
					
					new Thread(){
						
						public void run(){
							
							try {
								Thread.sleep(1000);
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
							Register(_id, _port, reg.get_emulport(), reg.getBattery(), reg.get_station_id());

						}
						
					}.start();
					
					while ((inline = InSimulator.readLine()) != null){
						
						System.out.println(inline);
						
						split = inline.split(" ");
						
						if(split[0].equals("Changes")){
							
							
							int batt = Integer.parseInt(split[1]);
							double alt = Double.parseDouble(split[2]);
							
					    	mDbHelper.setBattery(batt);
					    	mDbHelper.setAltitude(alt);
					    	
					    	if(batt < 10){
					    		
					    		mDbHelper.setState("OutOfBattery");
					    		
					    	}
					    	
							
							try {
								
								main.send(Message.obtain(null, mobile.Changes, batt, (int) alt));
							
							} catch (NumberFormatException e) {
								e.printStackTrace();
							} catch (RemoteException e) {
								e.printStackTrace();
							}
							
						}else if(split[0].equals("Near_Entity")){
							
							/** Near_Entity <id> <address> <port> **/
						
							if(!mDbHelper.getState().equals("onStation")){
							
								
							split[2] = treatAddress(split[2]);
							
							System.out.println("addres: "+split[2] );
							
								sendInformation(Integer.parseInt(split[1]), split[2], Integer.parseInt(split[3]));
							}
						}else if(split[0].equals("Address")){
							
							_address = split[1];
							
						}else if(split[0].equals("Far_Entity")){
							
							if(!mDbHelper.getState().equals("onStation")){
								
								/** Far_Entity <id> <address> <port> **/
								split[2] = treatAddress(split[2]);
								
								System.out.println("addres: "+split[2] );
								
								sendInformation(Integer.parseInt(split[1]), split[2], Integer.parseInt(split[3]));
							}
						}else if(split[0].equals("Alt_Change")){
							
							if(!mDbHelper.getState().equals("onStation")){
								/** Alt_Change <id> <address> <port> **/
								
								split[2] = treatAddress(split[2]);
								System.out.println("addres: "+split[2] );
								
								sendAltitudeChange(Integer.parseInt(split[1]), split[2], Integer.parseInt(split[3]));
							}
						
						}else if(split[0].equals("Normalize")){
							
							if(!mDbHelper.getState().equals("onStation"))
								normalize();
							
						}else if(split[0].equals("On_Station")){
							
							split[1] = treatAddress(split[1]);
							
							System.out.println("addres: "+split[1] );
							
							
							lastStation_addr = split[1];
							lastStation_port = Integer.parseInt(split[2]);
							
							if(!mDbHelper.getState().equals("onStation")){
								
								mDbHelper.setState("onStation");
								DatabaseSyncWebServiceUtil.syncDatabases(mDbHelper.getDBtoCentral());
							}
							sendArrivalMessage(split[1], Integer.parseInt(split[2]));
							
						}else if(split[0].equals("Near_Station") || split[0].equals("Far_Station")){
							
							//TODO descarregar aqui a base de dados
							System.out.println("Manda bd");
							DatabaseSyncWebServiceUtil.syncDatabases(mDbHelper.getDBtoCentral());
							System.out.println("Mandou bd");
						}	
						
					}
				} catch (NumberFormatException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
    	
		}
}
    
    /** Class netServer **/
    
    class NetServer extends Thread{

    	private ServerSocket socket;
    	private boolean state = true;
    	
    	public void close(){
    		
    		try {
				this.socket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			System.out.println("socket Closed");
    	}
    	
    	public NetServer(int port) {
        	super("NetServer");
        	
        	try {
        		
    			this.socket = new ServerSocket(port);
    			
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    	
    	public void run() {
        	
    		try {
    			
    			while (state)
    			
    				new NetServerHandler(this.socket.accept()).start();
    			
    			 
    		} catch (IOException e) {
    			System.out.println("ERRO");
    			e.printStackTrace();
    		}
    	
    	}
    	
    	/** Class netServer Handler **/
        
        class NetServerHandler extends Thread{

        	private Socket socket = null;

            public NetServerHandler(Socket socket) {
            	super("ClientThread");

            	this.socket = socket;
            }
            
            public void run(){

            		
            		try {
						InputStream in2 =  socket.getInputStream();
	        			
	        			Msg message = Msg.parseFrom(in2);
	        			
	        			this.socket.close();
	        			
	        			switch(message.getId()){
	        			
	        				case trip_msg:
	        					System.out.println("RECEBEU a mensagem");
	        					main.send(Message.obtain(null, mobile.RecievedTrip, message.getTrip()));
	        					break;
	        				case charge_msg:
	        					mDbHelper.setState("Charging");
	        					Charge a = message.getCharge();
	        					main.send(Message.obtain(null, mobile.Charge, a.getLimit(), 0));
	        					break;
	        				case Bd_msg:
	        						System.out.println("RECEBEU a mensagem");
	        						mDbHelper.updateDB(message.getBdMsg());
	        					break;
	        				case Change_Altitude:
	        					
	        					if(message.getAltitude().getAlt() == mDbHelper.getAltitude()
	        							&& _id < message.getAltitude().getId()){
	        						
	        						riseAltitude();
	        						
	        					}
	        					
	        					break;
	        			}	
					} catch (IOException e) {
						e.printStackTrace();
					} catch (RemoteException e) {
						e.printStackTrace();
					}

            }
	
        }

    }
}